//==============================================================================
//
//               Copyright (C) 2005  Dick van Oudheusden
//  
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public
// License along with this program; if not, write to the Free
// Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
//==============================================================================
// 
//  $Date: 2005-06-12 05:09:06 $ $Revision: 1.5 $
//
//==============================================================================

#include <stdlib.h>
#include <math.h>

#include "ofc/DFixedPoint.h"

#include "DInc.h"
#include "DTest.h"


//-Datatype--------------------------------------------------------------------

void DFixedPoint_test(void)
{
  DFixedPoint    *fp1    = [DFixedPoint new];
  DFixedPoint    *fp2    = [DFixedPoint alloc];
  DFixedPoint    *fp3    = [DFixedPoint alloc];
  
  STARTTEST();

  // Constructors and members
  TEST([fp1 point] == 0);
  
  [fp1 point :3];
  [fp1 set   :15];
  
  TEST([fp1 get     ] == 15);
  TEST([fp1 toDouble] == 1.875);
  
  [fp1 point :2]; // scale..
  
  TEST([fp1 get] == 7);

  TEST([[fp1 toText] ccompare :"1.75"] == 0);
  
  [fp2 init :15];
  
  [fp2 set :255];
  
  TEST([[fp2 toText] ccompare :"0.00778198"] == 0);

  // Addition
  [fp3 init :24 :4];
  
  TEST([fp3 get]   == 24);
  TEST([fp3 point] == 4);
  TEST([fp3 toDouble] == 1.5);
  
  [fp1 add :fp3]; // 1.75 + 1.5
  
  TEST([fp1 toDouble] == 3.25);
  TEST([fp1 point] == 2);
  
  [fp1 set :7 :2];
  [fp3 add :fp1]; // 1.5 + 1.75
  
  TEST([fp3 toDouble] == 3.25);
  TEST([fp3 point] == 4);
  
  [fp3 set :24 :4];
  [fp2 add :fp3 :fp1];
  
  TEST([fp2 toDouble] == 3.25);
  TEST([fp2 get     ] == 13);
  TEST([fp2 point   ] == 2);
  
  // Subtraction
  [fp1 set :24 :4]; // 1.5
  [fp2 set :7  :2]; // 1.75
  
  [fp1 sub :fp2]; // 1.5 - 1.75
  
  TEST([fp1 toDouble] == -0.25);
  TEST([fp1 get     ] == -4);
  TEST([fp1 point   ] == 4);

  [fp1 set :24 :4]; // 1.5
  [fp2 set :7  :2]; // 1.75
  
  [fp2 sub :fp1]; // 1.75 - 1.5
  
  TEST([fp2 toDouble] == 0.25);
  TEST([fp2 get     ] == 1);
  TEST([fp2 point   ] == 2);

  [fp3 point :15];
  
  [fp1 set :24 :4]; // 1.5
  [fp2 set :7  :2]; // 1.75
  
  [fp3 sub :fp1 :fp2];

  TEST([fp3 toDouble] == -0.25);
  TEST([fp3 get     ] == -4);
  TEST([fp3 point   ] == 4);

  // Multiplication
  [fp1 set :24 :4]; // 1.5
  [fp2 set :7  :2]; // 1.75

  [fp1 mul :fp2];
  
  TEST([fp1 toDouble] == 2.625);
  TEST([fp1 get     ] == 21);
  TEST([fp1 point   ] == 3);

  [fp1 set :24 :4]; // 1.5
  [fp2 set :7  :2]; // 1.75

  [fp2 mul :fp1];
  
  TEST([fp2 toDouble] == 2.625);
  TEST([fp2 get     ] == 21);
  TEST([fp2 point   ] == 3);

  [fp1 set :-24 :4]; // -1.5
  [fp2 set :7  :2]; // 1.75

  [fp3 mul :fp1 :fp2];
  
  TEST([fp3 toDouble] == -2.625);
  TEST([fp3 get     ] == -21);
  TEST([fp3 point   ] == 3);

  // Division
  [fp1 set :21 :4]; // 1.3125 
  [fp2 set :7  :2]; // 1.75
  
  [fp3 div :fp1 :fp2];
  
  TEST([fp3 toDouble] == 0.75);
  TEST([fp3 get     ] == 3);
  TEST([fp3 point   ] == 2);

  
  [fp1 set :-21 :4]; // -1.3125 
  [fp2 set :7   :2]; // 1.75
  
  [fp3 div :fp1 :fp2];
  
  TEST([fp3 toDouble] == -0.75);
  TEST([fp3 get     ] == -3);
  TEST([fp3 point   ] == 2);

  
  [fp1 set :21 :4]; // 1.3125 
  [fp2 set :-7 :2]; // -1.75
  
  [fp3 div :fp1 :fp2];
  
  TEST([fp3 toDouble] == -0.75);
  TEST([fp3 get     ] == -3);
  TEST([fp3 point   ] == 2);

  
  [fp1 set :21 :2]; // 5.25 
  [fp2 set :7  :2]; // 1.75
  
  [fp3 div :fp1 :fp2];
  
  TEST([fp3 toDouble] == 3);
  TEST([fp3 get     ] == 3);
  TEST([fp3 point   ] == 0);

  
  [fp1 set :21 :2]; // 5.25 
  [fp2 set :7  :4]; // 0.4375
  
  [fp3 div :fp1 :fp2];
  
  TEST([fp3 toDouble] == 12);
  TEST([fp3 get     ] == 12);
  TEST([fp3 point   ] == 0);

  
  [fp1 set :-21 :2]; // -5.25 
  [fp2 set :7   :4]; // 0.4375
  
  [fp3 div :fp1 :fp2];
  
  TEST([fp3 toDouble] == -12);
  TEST([fp3 get     ] == -12);
  TEST([fp3 point   ] == 0);

  
  [fp1 set :7  :2]; // 1.75
  [fp2 set :21 :2]; // 5.25
  
  [fp3 div :fp1 :fp2];
  
  TEST([fp3 toDouble] == 0); // actually 0.3333...
  TEST([fp3 get     ] == 0);
  TEST([fp3 point   ] == 0);

  [fp1 point :10]; // so, increase the precision...
  
  [fp3 div :fp1 :fp2];
  
  TEST(fabs([fp3 toDouble] - 0.332031) < 0.000001);
  TEST([fp3 get     ] == 85);
  TEST([fp3 point   ] == 8);

  
  // Compare
  [fp1 set :-7 :2]; // -1.75
  [fp2 set :24 :4]; //  1.5
  
  TEST([fp1 compare :fp2] < 0);
  TEST([fp2 compare :fp1] > 0);
  TEST([fp1 compare :fp1] == 0);
  TEST([fp2 compare :fp2] == 0);

  [fp1 set :7  :2]; //  1.75
  [fp2 set :28 :4]; //  1.75

  TEST([fp1 compare :fp2] == 0);
  TEST([fp2 compare :fp1] == 0);

  [fp1 set :-7  :2]; //  -1.75
  [fp2 set :-28 :4]; //  -1.75

  TEST([fp1 compare :fp2] == 0);
  TEST([fp2 compare :fp1] == 0);

  [fp1 set :7  :2]; //  1.75
  [fp2 set :24 :4]; //  1.5
  
  TEST([fp1 compare :fp2] > 0);
  TEST([fp2 compare :fp1] < 0);
  
  [fp1 set :-7  :2]; //  -1.75
  [fp2 set :-24 :4]; //  -1.5
  
  TEST([fp1 compare :fp2] < 0);
  TEST([fp2 compare :fp1] > 0);

  //printf("%s %ld %d\n", [[fp1 toText] cstring], [fp1 get], [fp1 point]);
  //printf("%s %ld %d\n", [[fp2 toText] cstring], [fp2 get], [fp2 point]);
  //printf("%s %ld %d\n", [[fp3 toText] cstring], [fp3 get], [fp3 point]);
  
  [fp1 free];
  [fp2 free];
  [fp3 free];
  
  STOPTEST();
}

